package io.youngledo.usb.storage.impl;

import com.volcengine.tos.TOSV2;
import com.volcengine.tos.TOSV2ClientBuilder;
import com.volcengine.tos.TOSClientConfiguration;
import com.volcengine.tos.auth.StaticCredentials;
import com.volcengine.tos.transport.TransportConfig;
import com.volcengine.tos.model.bucket.*;
import com.volcengine.tos.model.object.*;
import io.youngledo.usb.storage.*;
import io.youngledo.usb.exception.StorageConnectionException;
import io.youngledo.usb.exception.StorageOperationException;
import io.youngledo.usb.config.TimeoutConfig;
import io.youngledo.usb.task.ProgressTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/// 火山引擎TOS存储服务适配器，[地域和访问域名](https://www.volcengine.com/docs/6349/107356)
///
public class VolcEngineStorageService extends AbstractObjectStorageService {

    private static final Logger logger = LoggerFactory.getLogger(VolcEngineStorageService.class);

    private TOSV2 tosClient;
    private String region;

    @Override
    public void connect(StorageConfig config) {
        if (config.type() != StorageType.VOLC_ENGINE_TOS) {
            throw new IllegalArgumentException("Invalid storage type for Volcengine TOS service: " + config.type());
        }

        this.config = config;

        this.region = extractRegionFromEndpoint(config.endpoint());

        TransportConfig transportConfig = TransportConfig.builder()
                .connectTimeoutMills(TimeoutConfig.CONNECTION_TIMEOUT_MS)
                .readTimeoutMills(TimeoutConfig.READ_TIMEOUT_MS)
                .writeTimeoutMills(TimeoutConfig.WRITE_TIMEOUT_MS)
                .build();
        TOSClientConfiguration configuration = TOSClientConfiguration.builder()
                .transportConfig(transportConfig)
                .region(region)
                .endpoint(config.endpoint())
                .credentials(new StaticCredentials(config.accessKey(), config.secretKey()))
                .build();
        this.tosClient = new TOSV2ClientBuilder().build(configuration);

        // 测试连接 - 尝试列出桶来验证连接
        ListBucketsV2Input listBucketsInput = new ListBucketsV2Input();
        tosClient.listBuckets(listBucketsInput);

        this.connected = true;
    }

    private String extractRegionFromEndpoint(String endpoint) {
        // 从 endpoint 中提取区域信息
        // 标准格式: https://tos-{region}.volcengineapi.com
        if (endpoint.contains("tos-")) {
            String[] parts = endpoint.split("tos-");
            if (parts.length > 1) {
                return parts[1].split("\\.")[0];
            }
        }

        // 支持其他格式，如直接传region
        if (!endpoint.contains(".")) {
            return endpoint;
        }

        // 默认区域
        return "cn-beijing";
    }

    @Override
    protected void performDisconnect() {
        if (tosClient != null) {
            try {
                tosClient.close();
                logger.info("Disconnected from Volcengine TOS");
            } catch (Exception e) {
                logger.warn("Error during TOS client shutdown: {}", e.getMessage());
            }
            tosClient = null;
        }
        region = null;
    }

    @Override
    public List<StorageBucket> listBuckets() {
        checkConnection();
        return tosClient.listBuckets(new ListBucketsV2Input())
                .getBuckets().parallelStream()
                .map(bucket -> {
                    String creationDate = bucket.getCreationDate();
                    LocalDateTime localDateTime = LocalDateTime.ofInstant(
                        Instant.parse(creationDate),
                        ZoneId.systemDefault()
                    );
                    return StorageBucket.of(bucket.getName(), localDateTime);
                }).toList();
    }

    @Override
    public void createBucket(String bucketName) {
        checkConnection();
        CreateBucketV2Input input = new CreateBucketV2Input().setBucket(bucketName);
        tosClient.createBucket(input);
        logger.info("Successfully created bucket: {}", bucketName);
    }

    @Override
    public void deleteBucket(String bucketName) {
        checkConnection();
        DeleteBucketInput input = new DeleteBucketInput().setBucket(bucketName);
        tosClient.deleteBucket(input);
        logger.info("Successfully deleted bucket: {}", bucketName);
    }

    @Override
    public List<StorageObject> listObjects(String bucketName, String prefix, boolean recursive) {
        checkConnection();

        ListObjectsV2Input input = new ListObjectsV2Input()
                .setBucket(bucketName)
                .setPrefix(prefix)
                .setMaxKeys(1000);

        if (!recursive) {
            input.setDelimiter("/");
        }

        ListObjectsV2Output output = tosClient.listObjects(input);
        List<StorageObject> objects = new ArrayList<>();

        // 添加公共前缀（文件夹）
        if (output.getCommonPrefixes() != null) {
            objects.addAll(ObjectStorageHelper.processCommonPrefixes(
                    output.getCommonPrefixes().stream().map(ListedCommonPrefix::getPrefix).toList(),
                    prefix,
                    false  // 不移除末尾的斜杠
            ));
        }

        // 添加文件对象
        if (output.getContents() != null) {
            objects.addAll(ObjectStorageHelper.processObjects(
                    output.getContents(),
                    prefix,
                    ListedObjectV2::getKey,
                    (obj, key, displayName) -> {
                        // 如果是非递归模式，只显示当前层级的文件名
                        if (!recursive && displayName.contains("/")) {
                            displayName = displayName.substring(displayName.lastIndexOf('/') + 1);
                        }

                        LocalDateTime lastModified = obj.getLastModified()
                                .toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDateTime();

                        return new StorageObject(
                                key,
                                displayName,
                                obj.getSize(),
                                lastModified,
                                false,
                                obj.getEtag()
                        );
                    }
            ));
        }
        return objects;
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, File file) {
        checkConnection();

        try (FileInputStream fis = new FileInputStream(file)) {
            PutObjectInput input = new PutObjectInput()
                    .setBucket(bucketName)
                    .setKey(objectKey)
                    .setContent(fis)
                    .setContentLength(file.length());

            tosClient.putObject(input);
            logger.info("Successfully uploaded file {} to bucket {}", objectKey, bucketName);
        } catch (IOException e) {
            throw StorageOperationException.operationFailed("Failed to upload file: " + e.getMessage(), e);
        }
    }

    @Override
    public void uploadFile(String bucketName, String objectKey, InputStream inputStream, long contentLength) {
        checkConnection();

        PutObjectInput input = new PutObjectInput()
                .setBucket(bucketName)
                .setKey(objectKey)
                .setContent(inputStream)
                .setContentLength(contentLength);

        tosClient.putObject(input);
        logger.info("Successfully uploaded stream {} to bucket {}", objectKey, bucketName);
    }

    @Override
    public InputStream downloadFile(String bucketName, String objectKey) {
        checkConnection();

        GetObjectV2Input input = new GetObjectV2Input()
                .setBucket(bucketName)
                .setKey(objectKey);

        GetObjectV2Output output = tosClient.getObject(input);
        return output.getContent();
    }

    @Override
    public void downloadFile(String bucketName, String objectKey, File localFile) {
        checkConnection();
        try (InputStream inputStream = downloadFile(bucketName, objectKey);
             FileOutputStream outputStream = new FileOutputStream(localFile)) {
            inputStream.transferTo(outputStream);
        } catch (IOException e) {
            throw StorageOperationException.objectNotFound(objectKey);
        }
    }

    @Override
    public void deleteObject(String bucketName, String objectKey) {
        checkConnection();

        DeleteObjectInput input = new DeleteObjectInput()
                .setBucket(bucketName)
                .setKey(objectKey);

        tosClient.deleteObject(input);
        logger.info("Successfully deleted object {} from bucket {}", objectKey, bucketName);
    }

    @Override
    public void deleteObjects(String bucketName, List<String> objectKeys) {
        checkConnection();

        if (objectKeys.isEmpty()) {
            return;
        }

        List<ObjectTobeDeleted> objectsToDelete = new ArrayList<>();
        for (String key : objectKeys) {
            objectsToDelete.add(new ObjectTobeDeleted().setKey(key));
        }

        DeleteMultiObjectsV2Input input = new DeleteMultiObjectsV2Input()
                .setBucket(bucketName)
                .setObjects(objectsToDelete);

        tosClient.deleteMultiObjects(input);
        logger.info("Successfully deleted {} objects from bucket {}", objectKeys.size(), bucketName);
    }

    @Override
    public StorageObject getObjectInfo(String bucketName, String objectKey) {
        checkConnection();

        HeadObjectV2Input input = new HeadObjectV2Input()
                .setBucket(bucketName)
                .setKey(objectKey);

        HeadObjectV2Output output = tosClient.headObject(input);

        String displayName = objectKey;
        if (displayName.contains("/")) {
            displayName = displayName.substring(displayName.lastIndexOf('/') + 1);
        }

        LocalDateTime lastModified = output.getLastModifiedInDate()
                .toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        return new StorageObject(
                objectKey,
                displayName,
                output.getContentLength(),
                lastModified,
                false,
                output.getEtag()
        );
    }

    @Override
    public StorageType getStorageType() {
        return StorageType.VOLC_ENGINE_TOS;
    }

    @Override
    public String generatePresignedUrl(String bucketName, String objectKey, int expiry) {
        checkConnection();

        PreSignedURLInput input = new PreSignedURLInput()
                .setBucket(bucketName)
                .setKey(objectKey)
                .setHttpMethod(com.volcengine.tos.comm.HttpMethod.GET)
                .setExpires(expiry);

        PreSignedURLOutput output = tosClient.preSignedURL(input);
        return output.getSignedUrl();
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, File file, ProgressTracker progressTracker) {
        checkConnection();

        progressTracker.statusProperty().set("开始上传到火山引擎TOS...");

        // 火山引擎SDK支持进度回调，这里使用基本实现
        uploadFile(bucketName, objectKey, file);

        progressTracker.complete();
    }

    @Override
    public void uploadFileWithProgress(String bucketName, String objectKey, InputStream inputStream, long contentLength, ProgressTracker progressTracker) {
        checkConnection();

        progressTracker.statusProperty().set("开始上传到火山引擎TOS...");

        uploadFile(bucketName, objectKey, inputStream, contentLength);

        progressTracker.complete();
    }
}